<?php
declare(strict_types=1);

namespace App\Service\Web;

use App\Enum\Code;
use App\Exception\BusinessException;
use App\Mail\WelcomeMailWithText;
use App\Repository\Web\UserRepository;
use App\Repository\Web\UserSignInLogsRepository;
use Hyperf\Redis\Redis;
use HyperfExt\Jwt\Contracts\JwtFactoryInterface;
use HyperfExt\Jwt\Contracts\ManagerInterface;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Di\Annotation\Inject;

class UserSignInService
{
    private const SIGNIN_BITMAP_PREFIX = 'signin:bitmap:';
    private const SIGNIN_GLOBAL_PREFIX = 'signin:global:';
    protected RequestInterface $request;
    private $user_id;
    public function __construct(RequestInterface $request,JwtFactoryInterface $jwtFactory)
    {
        $this->request = $request;
        $payload = $jwtFactory->make()->getPayload();
        $this->user_id = (int)$payload->get('sub');
    }

    #[Inject]
    private WelcomeMailWithText $mailService;
    #[Inject]
    protected ManagerInterface $jwtManager;
    #[Inject]
    protected UserRepository $userRepository;
    #[Inject]
    protected Redis $redis;
    #[Inject]
    protected UserSignInLogsRepository $userSignInLogsRepository;

    /**
     * 用户签到
     */
    public function signIn(): array
    {
        $monthKey = $this->getMonthKey();
        $dayOffset = (int) date('j') - 1;
        $globalKey = $this->getGlobalKey();

        // 检查今日是否已签到
        if ($this->redis->getBit($monthKey, $dayOffset)) {
            throw new BusinessException(Code::SignedToday, '今日已签到');
        }

        // 使用管道执行原子操作
        $pipe = $this->redis->pipeline();
        $pipe->setBit($monthKey, $dayOffset, true);
        $pipe->setBit($globalKey, $this->user_id, true);
        $pipe->expire($monthKey, 60 * 60 * 24 * 60); // 保留2个月
        $pipe->expire($globalKey, 60 * 60 * 24); // 当日排行榜保留1天
        $pipe->exec();

        // 计算签到数据
        $continuousDays = $this->getContinuousDays();
        $totalDays = $this->getTotalSignInDays();
        $points = $this->calculatePoints($continuousDays);

        // 记录签到日志
        $this->recordSignInLog($points,$totalDays);

        return [
            'continuous_days' => $continuousDays,
            'total_days' => $totalDays,
            'points_earned' => $points,
            'signed_at' => date('Y-m-d H:i:s')
        ];
    }

    /**
     * 获取连续签到天数
     */
    public function getContinuousDays(): int
    {
        $currentMonth = date('Ym');
        $currentKey = $this->getMonthKey($currentMonth);
        $dayOffset = (int) date('j') - 1;

        $continuous = 0;

        // 当前月向前追溯
        for ($i = $dayOffset; $i >= 0; $i--) {
            if ($this->redis->getBit($currentKey, $i)) {
                $continuous++;
            } else {
                break;
            }
        }

        // 如果当前月全勤，检查上月连续情况
        if ($continuous === ($dayOffset + 1)) {
            $lastMonth = date('Ym', strtotime('last month'));
            $lastKey = $this->getMonthKey($lastMonth);
            $lastMonthDays = (int) date('t', strtotime('last month'));

            $continuous += $this->calculateLastMonthContinuous($lastKey, $lastMonthDays);
        }

        return $continuous;
    }

    /**
     * 获取总签到天数
     */
    public function getTotalSignInDays(): int
    {

        $pattern = self::SIGNIN_BITMAP_PREFIX . $this->user_id . ':*';
        $keys = $this->redis->keys($pattern);
        $total = 0;

        foreach ($keys as $key) {
            $total += $this->redis->bitCount($key);
        }

        return $total;
    }

    /**
     * 获取月度签到数据
     */
    public function getMonthSignInData(string $yearMonth): array
    {
        $key = $this->getMonthKey($yearMonth);
        $daysInMonth = (int) date('t', strtotime($yearMonth . '01'));

        $signInData = [];
        for ($i = 0; $i < $daysInMonth; $i++) {
            $signInData[$i + 1] = (bool) $this->redis->getBit($key, $i);
        }

        return $signInData;
    }

    /**
     * 获取当日签到排行榜
     */
    public function getTodayRank(int $limit = 100): array
    {
        $globalKey = $this->getGlobalKey();
        $userIds = [];
        $offset = 0;

        // 循环查找所有设置为1的位
        while (count($userIds) < $limit) {
            // 查找下一个设置为1的位
            $pos = $this->redis->bitPos($globalKey, true, $offset);
            if ($pos === -1) {
                break; // 没有更多设置的位
            }
            $userIds[] = $pos;
            $offset = $pos + 1;
        }

        return array_slice($userIds, 0, $limit);
    }

    private function getMonthKey(?string $month = null): string
    {
        $month = $month ?? date('Ym');
        return self::SIGNIN_BITMAP_PREFIX . $this->user_id . ':' . $month;
    }

    private function getGlobalKey(?string $date = null): string
    {
        $date = $date ?? date('Ymd');
        return self::SIGNIN_GLOBAL_PREFIX . $date;
    }

    private function calculateLastMonthContinuous(string $key, int $daysInMonth): int
    {
        $continuous = 0;
        for ($i = $daysInMonth - 1; $i >= 0; $i--) {
            if ($this->redis->getBit($key, $i)) {
                $continuous++;
            } else {
                break;
            }
        }
        return $continuous;
    }

    private function calculatePoints(int $continuousDays): int
    {
        $basePoints = 10;
        $continuousBonus = min($continuousDays * 2, 50); // 连续奖励最多50分

        return $basePoints + $continuousBonus;
    }

    private function recordSignInLog(int $points,int $totalDays): void
    {
        // 记录到MySQL用于数据持久化
        $this->userSignInLogsRepository->create(
            [
                'user_id' => $this->user_id,
                'points_earned' => $points,
                'signin_date' => date('Y-m-d'),
                'continuous_days' => $totalDays,
                'created_at' => date('Y-m-d H:i:s')
            ]
        );
        //        // 更新用户积分
        //        DB::table('users')
        //            ->where('id', $userId)
        //            ->increment('points', $points);
    }
}